from typing import Dict, List, Set, Tuple
from ..models import TestCase, TestExecution


class DependencyResolver:
    """测试用例依赖解析器"""

    def __init__(self):
        self.dependency_graph = {}  # 依赖图
        self.execution_order = []   # 执行顺序
        self.visited = set()        # 已访问的节点
        self.variables = {}         # 变量存储

    def resolve_dependencies(self, test_cases: List[TestCase]) -> Tuple[List[TestCase], Dict]:
        """
        解析测试用例依赖关系，返回排序后的测试用例列表和所需变量
        :param test_cases: 要执行的测试用例列表
        :return: (排序后的测试用例列表, 依赖变量映射)
        """
        # 构建依赖图
        self._build_dependency_graph(test_cases)
        
        # 检测循环依赖
        if self._has_circular_dependencies():
            raise ValueError("检测到循环依赖")
            
        # 拓扑排序
        self.execution_order = []
        self.visited = set()
        
        # 从每个未访问的节点开始深度优先搜索
        for case in test_cases:
            if case.id not in self.visited:
                self._topological_sort(case.id)
                
        # 反转列表得到正确的执行顺序
        self.execution_order.reverse()
        
        # 获取排序后的测试用例列表
        ordered_cases = []
        for case_id in self.execution_order:
            for case in test_cases:
                if case.id == case_id:
                    ordered_cases.append(case)
                    break
                    
        return ordered_cases, self.variables

    def _build_dependency_graph(self, test_cases: List[TestCase]):
        """构建依赖图"""
        self.dependency_graph = {}
        self.variables = {}
        
        for case in test_cases:
            self.dependency_graph[case.id] = {
                'dependencies': [],
                'strategy': 'all_success',
                'variables_needed': set()
            }
            
            if case.dependencies:
                deps = case.dependencies
                self.dependency_graph[case.id]['strategy'] = deps.get('strategy', 'all_success')
                
                for dep in deps.get('cases', []):
                    self.dependency_graph[case.id]['dependencies'].append({
                        'case_id': dep['case_id'],
                        'required': dep.get('required', True)
                    })
                    
                    # 记录需要的变量
                    variables = dep.get('variables', [])
                    self.dependency_graph[case.id]['variables_needed'].update(variables)
                    
                    # 更新变量映射
                    if dep['case_id'] not in self.variables:
                        self.variables[dep['case_id']] = set()
                    self.variables[dep['case_id']].update(variables)

    def _has_circular_dependencies(self) -> bool:
        """检测是否存在循环依赖"""
        visited = set()
        path = set()
        
        def dfs(node):
            visited.add(node)
            path.add(node)
            
            for dep in self.dependency_graph[node]['dependencies']:
                next_node = dep['case_id']
                if next_node not in self.dependency_graph:
                    continue
                if next_node in path:
                    return True
                if next_node not in visited:
                    if dfs(next_node):
                        return True
                        
            path.remove(node)
            return False
            
        for node in self.dependency_graph:
            if node not in visited:
                if dfs(node):
                    return True
        return False

    def _topological_sort(self, case_id: int):
        """拓扑排序（使用深度优先搜索）"""
        self.visited.add(case_id)
        
        if case_id in self.dependency_graph:
            for dep in self.dependency_graph[case_id]['dependencies']:
                next_case = dep['case_id']
                if next_case not in self.visited and next_case in self.dependency_graph:
                    self._topological_sort(next_case)
                    
        self.execution_order.append(case_id)

    @staticmethod
    def check_dependencies(case: TestCase, executions: Dict[int, TestExecution]) -> Tuple[bool, str]:
        """
        检查依赖的测试用例是否满足执行条件
        :param case: 当前测试用例
        :param executions: 依赖用例的执行记录 {case_id: TestExecution}
        :return: (是否满足依赖条件, 错误信息)
        """
        if not case.dependencies:
            return True, ""
            
        strategy = case.dependencies.get('strategy', 'all_success')
        required_success = 0
        total_required = 0
        
        for dep in case.dependencies.get('cases', []):
            case_id = dep['case_id']
            required = dep.get('required', True)
            
            if required:
                total_required += 1
            
            if case_id not in executions:
                if required:
                    return False, f"缺少必需的依赖用例 {case_id} 的执行记录"
                continue
                
            execution = executions[case_id]
            if execution.result == 'pass':
                required_success += 1
            elif required:
                return False, f"依赖的测试用例 {case_id} 执行失败"
                
        if strategy == 'all_success' and required_success < total_required:
            return False, "并非所有必需的依赖用例都执行成功"
        elif strategy == 'any_success' and required_success == 0 and total_required > 0:
            return False, "没有任何依赖用例执行成功"
            
        return True, ""

    @staticmethod
    def get_dependency_variables(case: TestCase, executions: Dict[int, TestExecution]) -> Dict:
        """
        从依赖的测试用例执行记录中获取变量
        :param case: 当前测试用例
        :param executions: 依赖用例的执行记录 {case_id: TestExecution}
        :return: 变量字典
        """
        variables = {}
        
        if not case.dependencies:
            return variables
            
        for dep in case.dependencies.get('cases', []):
            case_id = dep['case_id']
            if case_id not in executions:
                continue
                
            execution = executions[case_id]
            if not execution.response_data or 'extracted_variables' not in execution.response_data:
                continue
                
            # 只获取需要的变量
            needed_vars = dep.get('variables', [])
            extracted_vars = execution.response_data['extracted_variables']
            
            for var_name in needed_vars:
                if var_name in extracted_vars:
                    variables[var_name] = extracted_vars[var_name]
                    
        return variables 